package com.aosiding.miaosha.controller;

import com.aosiding.miaosha.service.OrderService;
import com.aosiding.miaosha.service.UserService;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.sql.Time;
import java.util.concurrent.TimeUnit;


/**
 * 接口限流：是对某一时间窗口内的请求数进行限制，保持系统的可用性和稳定性，
 *         防止因流量暴增而导致的系统运行缓慢或宕机
 * 比如有2000个线程，直接抛弃一些请求
 * 常见的限流算法有 令牌法 和 漏桶（漏斗算法）
 * 高并发时有三把利器用来保护系统：缓存，降级，限流
 */
@RestController
@RequestMapping("stock")
@Slf4j
public class StockController {

    @Autowired
    private OrderService orderService;
    //创建令牌桶实例
    private RateLimiter rateLimiter = RateLimiter.create(40);

    @Autowired
    private UserService userService;

    //生成md5值的方法
    @RequestMapping("md5")
    public String getMd5(Integer id, Integer userid) {
        String md5;
        try {
            md5 = orderService.getMd5(id, userid);
        }catch (Exception e){
            e.printStackTrace();
            return "获取md5失败: "+e.getMessage();
        }
        return "获取md5信息为: "+md5;
    }

    /**
     * 令牌桶简单使用
     */
    //创建令牌桶实例
//    private RateLimiter rateLimiter = RateLimiter.create(10);
//    @GetMapping("sale")
//    public String sale(Integer id){
//        //1.请求阻塞，没有获取到token的请求，一直获取，直到获取到token 令牌
//        //log.info("等待的时间："+rateLimiter.acquire());
//
//        //2.设置一个等待时间，如果在等待时间内获取到了token 令牌，则处理业务，
//        //  如果在等待时间内没有获取到响应token则抛弃
//        if (!rateLimiter.tryAcquire(5, TimeUnit.SECONDS)){
//            System.out.println("当前请求被限流，直接抛弃，无法调用后续秒杀逻辑");
//            return "抢购失败";
//        }
//        System.out.println("处理业务......");
//        return "抢购成功";
//    }

    /**
     *开发秒杀方法 使用乐观锁解决超卖
     */
    //开发秒杀方法
//    @GetMapping("kill")
//    public String kill(Integer id){
//        System.out.println("秒杀商品id："+id);
//        try {
//            //根据秒杀商品id，去调用秒杀业务
//            int orderId = orderService.kill(id);
//            return "秒杀成功，订单id为：" + String.valueOf(orderId);
//        }catch (Exception e){
//            e.printStackTrace();
//            return e.getMessage();
//        }
//    }

    /**
     *乐观锁＋防止超卖+令牌桶算法限流
     */
    //开发秒杀方法
//    @GetMapping("kill")
//    public String kill(Integer id){
//        System.out.println("秒杀商品id："+id);
//        //加入令牌桶的限流措施
//        //可能会导致商品剩余，没有卖完
//        if (rateLimiter.tryAcquire(3,TimeUnit.SECONDS)){
//            log.info("抛弃抢购请求");
//            return "抢购失败，当前秒杀活动过于火爆，请重试";
//        }
//        try {
//            //根据秒杀商品id，去调用秒杀业务
//            int orderId = orderService.kill(id);
//            return "秒杀成功，订单id为：" + String.valueOf(orderId);
//        }catch (Exception e){
//            e.printStackTrace();
//            return e.getMessage();
//        }
//    }

    /**
     *乐观锁＋防止超卖+令牌桶算法限流＋md5
     */
    //开发秒杀方法
    @GetMapping("kill")
    public String kill(Integer id,Integer userid,String md5){
        System.out.println("秒杀商品id："+id);
        //加入令牌桶的限流措施
        //可能会导致商品剩余，没有卖完
        if (!rateLimiter.tryAcquire(5,TimeUnit.SECONDS)){
            log.info("抛弃抢购请求");
            return "抢购失败，当前秒杀活动过于火爆，请重试";
        }
        try {
            //根据秒杀商品id，去调用秒杀业务
            int orderId = orderService.kill(id,userid,md5);
            return "秒杀成功，订单id为：" + String.valueOf(orderId);
        }catch (Exception e){
            e.printStackTrace();
            return e.getMessage();
        }
    }


    //开发秒杀方法乐观锁＋防止超卖+令牌桶算法限流＋md5签名（hash接口隐藏）+单用户访问频率限制
    @GetMapping("killtokenmd5limit")
    public String killtokenmd5limit(Integer id,Integer userid,String md5){
        System.out.println("秒杀商品id："+id);
        //加入令牌桶的限流措施
        //可能会导致商品剩余，没有卖完
        if (!rateLimiter.tryAcquire(5,TimeUnit.SECONDS)){
            log.info("抛弃抢购请求");
            return "抢购失败，当前秒杀活动过于火爆，请重试";
        }
        try {
            //单用户调用接口的频率限制
            int count = userService.saveUserCount(userid);
            log.info("用户截至该次的访问次数为:[{}]",count);
            //进行调用次数判断
            boolean isBanned = userService.getUserCount(userid);
            if (isBanned){
                log.info("购买失败，超过频率限制");
                return "购买失败，超过频率限制";
            }
            //根据秒杀商品id，去调用秒杀业务
            int orderId = orderService.kill(id,userid,md5);
            return "秒杀成功，订单id为：" + String.valueOf(orderId);
        }catch (Exception e){
            e.printStackTrace();
            return e.getMessage();
        }
    }

/** 开发秒杀方法 使用悲观锁解决超卖
 * synchronized是Java中的关键字，是一种同步锁。可修饰实例方法，静态方法，代码块。
 * synchronized是一种悲观锁
 * synchronized与@transaction冲突时，synchronized放在调用处即可
 * 同步代码块的方式并不好
 * 悲观锁方式不推荐
 */
//    @GetMapping("kill")
//    public String kill(Integer id){
//        System.out.println("秒杀商品id："+id);
//        try {
//            //根据秒杀商品id，去调用秒杀业务
//            synchronized (this){
//                int orderId = orderService.kill(id);
//                return "秒杀成功，订单id为：" + String.valueOf(orderId);
//            }
//        }catch (Exception e){
//            e.printStackTrace();
//            return e.getMessage();
//        }
//    }

    /**隐藏秒杀接口
     * 在一定的时间内执行秒杀处理，不能在任意时间都接受秒杀请求，应加入时间验证 Redis
     * 有人抓包获取我们接口地址，通过脚本抢购怎么办？                     Md5
     * 限制单用户的请求频率，即单位时间内限制访问次数                     Redis给每个用户做访问统计，甚至
     *                                                          带上商品id，对单个商品做访问统计
     */
}
